import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.UnmarshalException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.LinkedHashMap;

public class DBmanager extends java.rmi.server.UnicastRemoteObject implements
		ReceiveMessageInterface {

	private int thisPort;
	private int rows;
	private int columns;
	private int numtheaters;
	private boolean failure;
	private String thisAddress;
	private String baseLocation;
	private Registry registry;
	private FileOperator fileOps;
	private LinkedHashMap<Integer, Lugar> seatsMap;
	private LinkedHashMap<String, Integer> theatersMap;
	private final String dbRegistryName = "dbServer";

	public DBmanager() throws RemoteException {
		failure = false;
		String everything = "";

		// Open config file
		try {
			FileReader fr = new FileReader("config.txt");
			BufferedReader br = new BufferedReader(fr);

			StringBuilder sb = new StringBuilder();
			String line = br.readLine();

			while (line != null) {
				sb.append(line);
				sb.append('\n');
				line = br.readLine();
			}

			everything = sb.toString();
		} catch (IOException e) {
		}

		String args[] = everything.split("\\s+");

		// Load config file
		for (int i = 0; i < args.length; i++) {
			String[] lineChunks = args[i].split(":");
			if (lineChunks[0].equals("Port"))
				thisPort = Integer.parseInt(lineChunks[1]); // (registry port)
			else if (lineChunks[0].equals("Storage"))
				baseLocation = lineChunks[1];
			else if (lineChunks[0].equals("NumberTheaters"))
				numtheaters = Integer.parseInt(lineChunks[1]);
			else if (lineChunks[0].equals("Rows"))
				rows = Integer.parseInt(lineChunks[1]);
			else if (lineChunks[0].equals("Columns"))
				columns = Integer.parseInt(lineChunks[1]);
		}
		
		fileOps = new FileOperator(baseLocation);

		// generate theaters
		for (int i = 1; i <= numtheaters; i++)
			genTheater(i, rows, columns);

		// generate theaters translation hash
		genTheatersHash();
		// generate seats translation hash
		genSeatsHash();

		try {
			// get the address of this host.
			thisAddress = (InetAddress.getLocalHost()).toString();
		} catch (Exception e) {
			throw new RemoteException("can't get inet address.");
		}

		System.out.println("this address=" + thisAddress + ",port=" + thisPort);

		try {
			// create the registry and bind the name and object.
			registry = LocateRegistry.createRegistry(thisPort);
			registry.rebind(dbRegistryName, this);
			//registry.rebind("rmi://127.0.1.1:3232/dbServer", this);
		} catch (RemoteException e) {
			throw e;
		}
	}

	public ArrayList<String> listTheaters() throws RemoteException {

		ArrayList<String> theaters = new ArrayList<String>();
		String str;

		try {
			for (int i = 1; i <= numtheaters; i++) {
				if ((str = fileOps.getTheaterName(i)) != null)
					theaters.add(str);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return theaters;
	}

	public Teatro getTheater(String theaterName) throws RemoteException {

		int teatroID = theatersMap.get(theaterName);
		Teatro teatro = null;

		System.out.println("ENTROU!!");

		try {
			teatro = fileOps.getTheater(teatroID, columns);
		} catch (IOException e) {
			e.printStackTrace();
		}

		return teatro;
	}

	public boolean reserveSeat(String theaterName, int lugarID)
			throws RemoteException {

		int teatroID = theatersMap.get(theaterName);
		Lugar lugar = seatsMap.get(lugarID);
		boolean result = false;
		try {
			result = fileOps.reserveSeat(teatroID, lugar.row, lugar.column);
		} catch (IOException e) {
			e.printStackTrace();
		}

		return result;
	}

	public boolean purchaseSeat(String theaterName, int lugarID)
			throws RemoteException {

		int teatroID = theatersMap.get(theaterName);
		Lugar lugar = seatsMap.get(lugarID);
		boolean result = false;
		try {
			result = fileOps.purchaseSeat(teatroID, lugar.row, lugar.column);
		} catch (IOException e) {
			e.printStackTrace();
		}

		return result;
	}

	public boolean falha() throws RemoteException {
		shutDownServerRegistry(true);
		return failure = true;
	}

	public boolean isInFailure() {
		return failure;
	}

	public boolean restart() throws RemoteException {
		failure = false;
		restartServerRegistry();
		return (!failure);
	}

	private boolean genTheater(int teatroID, int rows, int columns) {
		boolean result = false;

		try {
			result = fileOps.genTheater(teatroID, rows, columns);
		} catch (IOException e) {
		}
		return result;
	}

	private void genSeatsHash() {
		seatsMap = new LinkedHashMap<Integer, Lugar>(500);
		int id = 1;

		for (int i = 0; i < rows; i++) {
			for (int j = 1; j <= columns; j++) {
				Lugar lugar = new Lugar(id, i, j, Lugar.STATE_FREE);
				// System.out.println("gen: " + id);
				seatsMap.put(id, lugar);
				id++;

			}
		}
	}

	private void genTheatersHash() {
		theatersMap = new LinkedHashMap<String, Integer>(500);
		String name;

		for (int i = 1; i <= numtheaters; i++) {
			name = "teatro" + Integer.toString(i);
			theatersMap.put(name, i);
		}
	}

	private void shutDownServerRegistry(boolean failure) throws RemoteException {
		int succesful = 0;

		if (!failure)
			try {
				registry.unbind(dbRegistryName);
				UnicastRemoteObject.unexportObject(this, true);
			} catch (NotBoundException e) {
				succesful = -1;
			} catch (AccessException e) {
				succesful = -1;
			} catch (UnmarshalException e) {
				System.out.println(e.detail.getMessage());
				succesful = -1;
			} catch (RemoteException e) {
				System.out.println(e.detail.getMessage());
				succesful = -1;
			}
		else {
			try {
				registry.rebind(dbRegistryName + "temp", this);
			} catch (RemoteException e) {
				throw e;
			}
		}

		// System.exit(succesful);
	}

	private void restartServerRegistry() throws RemoteException {
		try {
			registry.rebind(dbRegistryName, this);
		} catch (RemoteException e) {
			throw e;
		}
	}

	public static void main(String args[]) {

		try {
			DBmanager db = new DBmanager();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

}
